/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 */
package org.olap4j.mdx;

import org.olap4j.OlapException;
import org.olap4j.mdx.parser.MdxValidator;
import org.olap4j.type.Type;
import org.olap4j.type.TypeUtil;

import java.util.List;
import java.util.Stack;

/**
 * Visitor which passes over a tree of MDX nodes, checks that they are valid, and assigns a type to each.
 * 
 * <p>
 * NOTE: This class is experimental. Not part of the public olap4j API.
 * 
 * @author jhyde
 * @since Jun 4, 2007
 */
class DefaultMdxValidatorImpl implements ParseTreeVisitor<ParseTreeNode>, MdxValidator {
	private Stack<Boolean> scalarStack = new Stack<Boolean>();
	private final SelectNode selectNode;

	/**
	 * Creates a DefaultMdxValidatorImpl.
	 * 
	 * @param selectNode
	 *            Root of parse tree
	 */
	protected DefaultMdxValidatorImpl(SelectNode selectNode) {
		this.selectNode = selectNode;
	}

	public SelectNode validateSelect(SelectNode selectNode) throws OlapException {
		return null;
	}

	public ParseTreeNode visit(SelectNode selectNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(AxisNode axis) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(WithMemberNode withMemberNode) {
		ParseTreeNode expression = acceptScalar(withMemberNode.getExpression());
		withMemberNode.setExpression(expression);
		final Type type = expression.getType();
		if (!TypeUtil.canEvaluate(type)) {
			throw new RuntimeException("'Member expression '" + MdxUtil.toString(expression) + "' must not be a set'");
		}
		for (PropertyValueNode prop : withMemberNode.getMemberPropertyList()) {
			prop.accept(this);
		}
		return withMemberNode;
	}

	public ParseTreeNode visit(WithSetNode withSetNode) {
		ParseTreeNode expression = acceptScalar(withSetNode.getExpression());
		withSetNode.setExpression(expression);
		final Type type = expression.getType();
		if (!TypeUtil.isSet(type)) {
			final IdentifierNode id = withSetNode.getIdentifier();
			final String idString = MdxUtil.toString(id);
			throw new RuntimeException("Set expression '" + idString + "' must be a set");
		}
		return withSetNode;
	}

	public ParseTreeNode visit(CallNode call) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(IdentifierNode id) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(ParameterNode parameterNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(CubeNode cubeNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(DimensionNode dimensionNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(HierarchyNode hierarchyNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(LevelNode levelExpr) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(MemberNode memberNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(LiteralNode literalNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(PropertyValueNode propertyValueNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public ParseTreeNode visit(DrillThroughNode drillThroughNode) {
		if (false) {
			return null;
		}
		throw new UnsupportedOperationException();
	}

	public void accept(AxisNode axis) {
		ParseTreeNode exp = axis.getExpression().accept(this);
		final Type type = exp.getType();
		if (!TypeUtil.isSet(type)) {
			throw new RuntimeException("Axis '" + axis.getAxis().name() + "' expression is not a set");
		}
	}

	public ParseTreeNode acceptScalar(ParseTreeNode node) {
		scalarStack.push(Boolean.TRUE);
		try {
			return node.accept(this);
		} finally {
			scalarStack.pop();
		}
	}

	// from IdentifierNode
	public ParseTreeNode accept(IdentifierNode identifier) {
		if (identifier.getSegmentList().size() == 1) {
			final IdentifierSegment s = identifier.getSegmentList().get(0);
			if (s.getQuoting() == Quoting.UNQUOTED && isReserved(s.getName())) {
				return LiteralNode.createSymbol(s.getRegion(), s.getName().toUpperCase());
			}
		}
		final ParseTreeNode element = lookup(selectNode, identifier.getSegmentList(), true);
		if (element == null) {
			return null;
		}
		return element.accept(this);
	}

	public ParseTreeNode accept(LiteralNode literalNode) {
		return literalNode;
	}

	public boolean isReserved(String name) {
		// todo: implement
		throw new UnsupportedOperationException();
	}

	private ParseTreeNode lookup(SelectNode select, List<IdentifierSegment> segments, boolean allowProp) {
		// todo: something like this:
		// final Exp element = Util.lookup(select, names, true);
		throw new UnsupportedOperationException();
	}
}

// End DefaultMdxValidatorImpl.java
